#ifndef __GLF_TASK_TASKDIRECTOR_IMPL_H_INCLUDED__
#define __GLF_TASK_TASKDIRECTOR_IMPL_H_INCLUDED__

#include <glf/task/taskDirector.h>
#include <glf/task/taskThread.h>
#include <glf/core/threadTraits.h>
#include <glf/core/thread.h>
#include <list>
#include <map>

namespace glf {

class TaskHandlerBase;

template<typename Tag1>
void TaskDirector::StartThread(int n, TaskThreadData& data)
{
	StartThread< TaskThread<Tag1, null_t> >(GetList<Tag1>(), n, data);
}

template<typename Tag1, typename Tag2>
void TaskDirector::StartThread(int n, TaskThreadData& data)
{
	StartThread< TaskThread<Tag1, Tag2> >(GetList<Tag1, Tag2>(), n, data);
}

template<typename ThreadType>
void TaskDirector::StartThread(ThreadList& tl, int n, TaskThreadData& data)
{
	GLF_ASSERT(n > 0);

	if(tl.m_Count < n)
	{
		for(int i = tl.m_Count; i < n; ++i)
		{
			TaskThreadImpl* t = new ThreadType(data.ptr);
			t->SetCoreAffinity(data.coreAffinityMask);
			
			t->Start();

			tl.m_Thread[i] = t;
		}

		tl.m_Count = n;
	}
	else if(tl.m_Count > n)
	{
		for(int i = n; i < tl.m_Count; ++i)
		{
			tl.m_Thread[i]->Stop();
			m_DeleteList.push_back(tl.m_Thread[i]);
		}

		tl.m_Count = n;
	}
}

template<typename Tag1>
void TaskDirector::StartThread(int n, void* ptr)
{
	TaskThreadData data = {
		ptr, -1
	};
	StartThread<Tag1>(n, data);
}

template<typename Tag1, typename Tag2>
void TaskDirector::StartThread(int n, void* ptr)
{
	TaskThreadData data = {
		ptr, -1
	};
	StartThread<Tag1, Tag2>(n, data);
}

template<typename ThreadType>
void TaskDirector::StartThread(ThreadList& tl, int n, void* ptr)
{
	TaskThreadData data = {
		ptr, -1
	};	
	StartThread<ThreadType>(tl, n, data);
}

template<typename Tag1>
void TaskDirector::StopThread()
{
	StopThread(GetList<Tag1>());
}

template<typename Tag1, typename Tag2>
void TaskDirector::StopThread()
{
	StopThread(GetList<Tag1, Tag2>());
}

template<typename Tag1>
int TaskDirector::GetThreadCount()
{
	return GetList<Tag1>().m_Count;
}

template<typename Tag1, typename Tag2>
int TaskDirector::GetThreadCount()
{
	return GetList<Tag1, Tag2>().size();
}

// IsDone return true if there is no task pending and there is no task currently executing for a certain tag
template<typename Tag>
bool TaskDirector::IsDone()
{
	return !TaskManager::GetInstance<Tag>().IsWorking() && !TaskManager::GetInstance<Tag>().HasPendingTask();
}

template<typename Tag1, typename Tag2>
bool TaskDirector::IsDone()
{
	return !TaskManager::GetInstance<Tag1>().IsWorking() && !TaskManager::GetInstance<Tag1>().HasPendingTask() &&
		    !TaskManager::GetInstance<Tag2>().IsWorking() && !TaskManager::GetInstance<Tag2>().HasPendingTask();
}

template<typename Tag>
void TaskDirector::ForceImmediateTaskExecution(bool executeImmediately)
{
	TaskManager::GetInstance<Tag>().ForceImmediateTaskExecution(executeImmediately);
}

template<typename Tag>
void TaskDirector::PauseTasksConsumption()
{
	TaskManager::GetInstance<Tag>().PauseTasksConsumption();
	while(TaskManager::GetInstance<Tag>().IsWorking())
	{
		glf::Thread::Sleep(1);
	}
}

template<typename Tag>
void TaskDirector::ResumeTasksConsumption()
{
	TaskManager::GetInstance<Tag>().ResumeTasksConsumption();
}

template<typename Tag1>
TaskDirector::ThreadList& TaskDirector::GetList()
{
	return Holder<Tag1, null_t>::GetList();
}

template<typename Tag1, typename Tag2>
TaskDirector::ThreadList& TaskDirector::GetList()
{
	return Holder<Tag1, Tag2>::GetList();
}

template<typename Tag1, typename Tag2>
TaskDirector::ThreadList& TaskDirector::Holder<Tag1, Tag2>::GetList()
{
	if(!s_ThreadListInstance)
	{
		while(AtomicCompareAndSwap(s_Lock, 1, 0) != 0) 
		{
			Thread::Sleep(1);
		}

		if(!s_ThreadListInstance)
		{
			ThreadList* temp = new ThreadList;
			Membar();
			s_ThreadListInstance = temp;
		}

		StoreWithRelease(s_Lock, 0);
	}

	return *s_ThreadListInstance;
}

} // end namespace glf

#endif // __GLF_TASK_TASKDIRECTOR_IMPL_H_INCLUDED__
